﻿Namespace MapObjects

    Public Class Models

        Public Structure IndexData
            Public Vertex As Integer
            Public TextCoord As Integer
            Public Normal As Integer
        End Structure

        Public Structure ModelObject
            Public ObjectName As String
            Public GroupName As String
            Public TextureName As String
            Public Positions As List(Of Vector3)
            Public TexCoords As List(Of Vector2)
            Public Normals As List(Of Vector3)
            Public Indices As List(Of IndexData)
            Public MaterialFileName As String
            Public ModelTexture As Texture2D
            Public ModelVertices As List(Of VertexPositionNormalTexture)
            Public VertexBuffer As VertexBuffer
            Public IndexBuffer As IndexBuffer
            Public ModelLocation As Vector3
            Public ModelRotation As Single
            Public QuadRotation As Quaternion
            Public WorldMatrix As Matrix
            Public ModelPartParent As Integer
            Public ModelPartID As Integer
        End Structure

        Private ModMeshImporter As Processors.OBJMesh
        Private ModelObjects() As ModelObject
        Private G As GraphicsDeviceManager
        Public ModelRotation As Single = 0
        Private World As Matrix = Matrix.Identity
        Public ModelLocation As Vector3 = Vector3.Zero
        Public ModelVelocity As Vector3 = Vector3.Zero
        Private OldRotation As Single = 0
        Private OldLocation As Vector3 = Vector3.Zero
        Private OldVelocity As Vector3 = Vector3.Zero
        Private IsCharModel As Boolean

        Public Sub New(Graphics As GraphicsDeviceManager, ContentDirectory As String)
            G = Graphics
            ModMeshImporter = New Processors.OBJMesh(G, ContentDirectory)
        End Sub

        Public Sub ImportOBJFile(FileName As String, Scale As Single)
            ModMeshImporter.ImportOBJFile(FileName, Scale)
            ModelObjects = ModMeshImporter.GetModelObjects()
            IsCharModel = IsCharacterModel(ModelObjects)
            If IsCharModel = True Then
                SetModelParents()
            End If
            Call CompileModels2()
        End Sub

        Private Function IsCharacterModel(ModObjects() As ModelObject) As Boolean
            Dim i As Integer
            For i = 0 To ModObjects.Count - 1
                If ModObjects(i).GroupName = "Stomach" Then
                    Return True
                End If
            Next i
            Return False
        End Function

        Private Sub SetModelParents()
            Dim i As Integer
            For i = 0 To ModelObjects.Count - 1
                Select Case ModelObjects(i).GroupName
                    Case "Stomach"
                        ModelObjects(i).ModelPartParent = 0
                        ModelObjects(i).ModelPartID = 1
                    Case "Chest"
                        ModelObjects(i).ModelPartParent = 1
                        ModelObjects(i).ModelPartID = 2
                    Case "LeftThigh"
                        ModelObjects(i).ModelPartParent = 1
                        ModelObjects(i).ModelPartID = 3
                    Case "RightThigh"
                        ModelObjects(i).ModelPartParent = 1
                        ModelObjects(i).ModelPartID = 4
                    Case "Neck"
                        ModelObjects(i).ModelPartParent = 2
                        ModelObjects(i).ModelPartID = 5
                    Case "LeftArm"
                        ModelObjects(i).ModelPartParent = 2
                        ModelObjects(i).ModelPartID = 6
                    Case "RightArm"
                        ModelObjects(i).ModelPartParent = 2
                        ModelObjects(i).ModelPartID = 7
                    Case "LeftLeg"
                        ModelObjects(i).ModelPartParent = 3
                        ModelObjects(i).ModelPartID = 8
                    Case "RightLeg"
                        ModelObjects(i).ModelPartParent = 4
                        ModelObjects(i).ModelPartID = 9
                    Case "HeadLeftEar"
                        ModelObjects(i).ModelPartParent = 5
                        ModelObjects(i).ModelPartID = 10
                    Case "RightEar"
                        ModelObjects(i).ModelPartParent = 10
                        ModelObjects(i).ModelPartID = 11
                    Case "LeftEye"
                        ModelObjects(i).ModelPartParent = 10
                        ModelObjects(i).ModelPartID = 12
                    Case "RightEye"
                        ModelObjects(i).ModelPartParent = 10
                        ModelObjects(i).ModelPartID = 13
                    Case "Nose"
                        ModelObjects(i).ModelPartParent = 10
                        ModelObjects(i).ModelPartID = 14
                    Case "Mouth"
                        ModelObjects(i).ModelPartParent = 10
                        ModelObjects(i).ModelPartID = 15
                    Case "LeftEyeBall"
                        ModelObjects(i).ModelPartParent = 12
                        ModelObjects(i).ModelPartID = 16
                    Case "RightEyeBall"
                        ModelObjects(i).ModelPartParent = 13
                        ModelObjects(i).ModelPartID = 17
                    Case "LeftForearm"
                        ModelObjects(i).ModelPartParent = 6
                        ModelObjects(i).ModelPartID = 18
                    Case "RightForearm"
                        ModelObjects(i).ModelPartParent = 7
                        ModelObjects(i).ModelPartID = 19
                    Case "LeftFoot"
                        ModelObjects(i).ModelPartParent = 8
                        ModelObjects(i).ModelPartID = 20
                    Case "RightFoot"
                        ModelObjects(i).ModelPartParent = 9
                        ModelObjects(i).ModelPartID = 21
                    Case "LeftHand"
                        ModelObjects(i).ModelPartParent = 18
                        ModelObjects(i).ModelPartID = 22
                    Case "RightHand"
                        ModelObjects(i).ModelPartParent = 19
                        ModelObjects(i).ModelPartID = 23
                    Case "LeftFootNails"
                        ModelObjects(i).ModelPartParent = 20
                        ModelObjects(i).ModelPartID = 24
                    Case "RightFootNails"
                        ModelObjects(i).ModelPartParent = 21
                        ModelObjects(i).ModelPartID = 25
                    Case "LeftHandNails"
                        ModelObjects(i).ModelPartParent = 22
                        ModelObjects(i).ModelPartID = 26
                    Case "RightHandNails"
                        ModelObjects(i).ModelPartParent = 23
                        ModelObjects(i).ModelPartID = 26
                End Select
            Next i
        End Sub


        Public Sub Update(TimeDiff As Single, KeyState As KeyboardState, MoState As MouseState)
            Dim i As Integer
            If IsCharModel = True Then
                If KeyState.IsKeyDown(Keys.A) Then
                    ModelRotation = ModelRotation + TimeDiff
                    CopyTransforms(0, ModelRotation * 0.5)
                    'SetModelRotation(1, ModelRotation)
                ElseIf KeyState.IsKeyDown(Keys.D) Then
                    ModelRotation = ModelRotation - TimeDiff
                    CopyTransforms(0, ModelRotation * 0.5)
                    'SetModelRotation(1, ModelRotation)
                ElseIf KeyState.IsKeyDown(Keys.W) And KeyState.IsKeyDown(Keys.LeftShift) Then
                    ApplyTranslation(False, True)
                ElseIf KeyState.IsKeyDown(Keys.W) Then
                    ApplyTranslation(False, False)
                ElseIf KeyState.IsKeyDown(Keys.S) Then
                    ApplyTranslation(True, True)
                ElseIf KeyState.IsKeyDown(Keys.None) Then
                    ModelVelocity = Vector3.Zero
                End If
                If OldLocation <> ModelLocation Or OldRotation <> ModelRotation Or OldVelocity <> ModelVelocity Then
                    For i = 0 To ModelObjects.Count - 1
                        RepositionVerts(ModelObjects(i))
                    Next i
                    World = Matrix.CreateFromAxisAngle(Vector3.UnitY, ModelRotation) * Matrix.CreateTranslation(ModelLocation)
                    OldLocation = ModelLocation
                    OldVelocity = ModelVelocity
                    OldRotation = ModelRotation
                End If
            End If
        End Sub

        Private Sub ApplyTranslation(Backward As Boolean, IsWalk As Boolean)
            Dim VelocityAdd As Vector3 = Vector3.Zero
            If Backward = True Then
                VelocityAdd.X = CSng(Math.Sin(ModelRotation))
                VelocityAdd.Z = CSng(Math.Cos(ModelRotation))
            Else
                VelocityAdd.X = -CSng(Math.Sin(ModelRotation))
                VelocityAdd.Z = -CSng(Math.Cos(ModelRotation))
            End If
            If IsWalk = True Then
                VelocityAdd = VelocityAdd * 0.5
            Else
                VelocityAdd = VelocityAdd * 0.75
            End If
            ModelVelocity = VelocityAdd
            ModelLocation = ModelLocation + ModelVelocity
        End Sub

        Private Sub RepositionVerts(ByRef MO As ModelObject)
            Dim i As Integer
            Dim VT As New List(Of VertexPositionNormalTexture)
            Dim tmp As VertexPositionNormalTexture
            For i = 0 To MO.ModelVertices.Count - 1
                tmp = MO.ModelVertices(i)
                tmp.Position = MO.ModelVertices(i).Position + ModelVelocity
                VT.Add(tmp)
            Next i
            MO.ModelVertices = VT
            MO.VertexBuffer = New VertexBuffer(G.GraphicsDevice, VertexPositionNormalTexture.VertexDeclaration, VT.Count, BufferUsage.WriteOnly)
            MO.VertexBuffer.SetData(Of VertexPositionNormalTexture)(VT.ToArray)
        End Sub

        Public Sub SetStaticModelRotation(ModRotation As Single)
            Dim i As Integer
            For i = 0 To ModelObjects.Count - 1
                ModelObjects(0).WorldMatrix = Matrix.CreateFromAxisAngle(Vector3.UnitY, ModRotation)
                RepositionVerts(ModelObjects(i))
            Next i
        End Sub

        Private Sub CopyTransforms(ModelPartID As Integer, ModRotation As Single)
            Dim ChildFound As Boolean = False
            Dim ID As Integer, i As Integer
            'Dim Rot As Quaternion
            For ID = 0 To ModelObjects.Count - 1
                If ModelObjects(ID).ModelPartID = ModelPartID Then
                    Exit For
                End If
            Next ID
            For i = 0 To ModelObjects.Count - 1
                ChildFound = False
                If ModelObjects(i).ModelPartParent = ModelPartID Then
                    'Rot = Quaternion.CreateFromAxisAngle(Vector3.UnitY, ModRotation)
                    'ModelObjects(i).QuadRotation = ModelObjects(i).QuadRotation
                    ModelObjects(i).WorldMatrix = Matrix.CreateFromAxisAngle(Vector3.UnitY, ModRotation)
                    CopyTransforms(ModelObjects(i).ModelPartID, ModRotation)
                End If
            Next i

        End Sub

        Private Sub CompileModels2()
            Dim i As Integer, j As Integer
            Dim ID() As IndexData
            Dim NewID() As Integer
            Dim Verts As List(Of VertexPositionNormalTexture)
            Dim IndList As List(Of Integer)
            Dim V() As Vector3
            Dim TC() As Vector2
            Dim N() As Vector3
            Dim VT() As VertexPositionNormalTexture
            For i = 0 To ModelObjects.Count - 1
                ReDim V(ModelObjects(i).Positions.Count - 1)
                For j = 0 To ModelObjects(i).Positions.Count - 1
                    V(j) = ModelObjects(i).Positions(j) + ModelLocation
                Next j
                ReDim TC(ModelObjects(i).TexCoords.Count - 1)
                For j = 0 To ModelObjects(i).TexCoords.Count - 1
                    TC(j) = ModelObjects(i).TexCoords(j)
                Next j
                ReDim N(ModelObjects(i).Normals.Count - 1)
                For j = 0 To ModelObjects(i).Normals.Count - 1
                    N(j) = ModelObjects(i).Normals(j)
                Next j
                ReDim ID(ModelObjects(i).Indices.Count - 1)
                For j = 0 To ModelObjects(i).Indices.Count - 1
                    ID(j) = ModelObjects(i).Indices(j)
                Next j
                FixVertexData(ID, NewID, VT, V, TC, N)

                Verts = New List(Of VertexPositionNormalTexture)
                IndList = New List(Of Integer)

                For j = 0 To VT.Count - 1
                    Verts.Add(VT(j))
                Next
                For j = 0 To NewID.Count - 1
                    IndList.Add(NewID(j))
                Next j
                ModelObjects(i).ModelVertices = Verts
                ModelObjects(i).VertexBuffer = New VertexBuffer(G.GraphicsDevice, VertexPositionNormalTexture.VertexDeclaration, Verts.Count, BufferUsage.WriteOnly)
                ModelObjects(i).VertexBuffer.SetData(Of VertexPositionNormalTexture)(Verts.ToArray)
                ModelObjects(i).IndexBuffer = New IndexBuffer(G.GraphicsDevice, IndexElementSize.ThirtyTwoBits, IndList.Count, BufferUsage.WriteOnly)
                ModelObjects(i).IndexBuffer.SetData(IndList.ToArray)
            Next i

        End Sub

        Private Sub FixVertexData(OldID() As IndexData, ByRef IDs() As Integer, ByRef VPNT() As VertexPositionNormalTexture, V() As Vector3, TC() As Vector2, N() As Vector3)
            Dim i As Integer, j As Integer
            Dim NewCt As Integer = -1
            Dim FoundID As Boolean = False
            Dim IDRef() As Integer, IDCt As Integer = -1
            Dim NewID() As IndexData
            For i = 0 To OldID.Count - 1
                If i = 0 Then
                    NewCt = NewCt + 1
                    ReDim Preserve NewID(NewCt)
                    NewID(NewCt).Vertex = OldID(i).Vertex
                    NewID(NewCt).TextCoord = OldID(i).TextCoord
                    NewID(NewCt).Normal = OldID(i).Normal
                    IDCt = IDCt + 1
                    ReDim Preserve IDRef(IDCt)
                    IDRef(IDCt) = NewCt

                Else
                    FoundID = False
                    For j = 0 To NewID.Count - 1
                        If NewID(j).Vertex = OldID(i).Vertex And NewID(j).TextCoord = OldID(i).TextCoord And NewID(j).Normal = OldID(i).Normal Then
                            FoundID = True
                            IDCt = IDCt + 1
                            ReDim Preserve IDRef(IDCt)
                            IDRef(IDCt) = j
                            Exit For
                        End If
                    Next j
                    If FoundID = False Then
                        NewCt = NewCt + 1
                        ReDim Preserve NewID(NewCt)
                        NewID(NewCt).Vertex = OldID(i).Vertex
                        NewID(NewCt).TextCoord = OldID(i).TextCoord
                        NewID(NewCt).Normal = OldID(i).Normal
                        IDCt = IDCt + 1
                        ReDim Preserve IDRef(IDCt)
                        IDRef(IDCt) = NewCt
                    End If
                End If
            Next i
            ReDim VPNT(NewID.Count - 1)
            For i = 0 To NewID.Count - 1
                VPNT(i).Position = V(NewID(i).Vertex)
                If NewID(i).TextCoord = -999 Then
                    VPNT(i).TextureCoordinate = Vector2.Zero
                Else
                    VPNT(i).TextureCoordinate = TC(NewID(i).TextCoord)
                End If
                If NewID(i).Normal = -999 Then
                    VPNT(i).Normal = Vector3.Zero
                Else
                    VPNT(i).Normal = N(NewID(i).Normal)
                End If
            Next i
            IDs = IDRef
        End Sub

        Public Sub Draw(Device As GraphicsDevice, Effect As Effect, Camera As Cameras.FirstPerson, LightDirection As Vector3, LightAmbientValue As Single)
            Dim i As Integer
            For i = 0 To ModelObjects.Count - 1
                Effect.CurrentTechnique = Effect.Techniques("Textured")
                Effect.Parameters("xWorld").SetValue(World * ModelObjects(i).WorldMatrix)
                Effect.Parameters("xView").SetValue(Camera.GetViewMatrix)
                Effect.Parameters("xProjection").SetValue(Camera.GetProjMatrix)
                Effect.Parameters("xEnableLighting").SetValue(True)
                Effect.Parameters("xLightDirection").SetValue(LightDirection)
                Effect.Parameters("xAmbient").SetValue(LightAmbientValue)
                Effect.Parameters("xTexture").SetValue(ModelObjects(i).ModelTexture)
                For Each pass As EffectPass In Effect.CurrentTechnique.Passes
                    pass.Apply()
                    Device.SetVertexBuffer(ModelObjects(i).VertexBuffer)
                    Device.Indices = ModelObjects(i).IndexBuffer
                    Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, ModelObjects(i).Indices.Count, 0, CInt(ModelObjects(i).Indices.Count / 3))
                    Device.SetVertexBuffer(Nothing)
                    Device.Indices = Nothing
                Next
            Next i
        End Sub

    End Class

End Namespace